/*
	bin2mot converts a binary file to Motorola hex.
	Copyright (C) 2012  pioneer suntec
	2012/08/02 leichang
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define REC_LINE_SIZE		(0x20)
#define MAX_LINE_SIZE		(256)

typedef enum{
	FALSE = 0
,	TRUE
}boolean;

typedef enum{
	MOTREC_MODE_S0 = 0
,	MOTREC_MODE_S1
,	MOTREC_MODE_S2
,	MOTREC_MODE_S3
,	MOTREC_MODE_S5 = 5
,	MOTREC_MODE_S7 = 7
,	MOTREC_MODE_S8
,	MOTREC_MODE_S9
,	MOTREC_MODE_UNKNOWN = 0xFF
}MOTREC_MODE;

unsigned char const SEPERATOR = '-';
unsigned char const version_info[] = "Ver1.0.1";

static void usage(void);
static void write_mot_file(FILE *file, MOTREC_MODE ucMotRecMode, unsigned char *ucData, unsigned char ucSize, unsigned long ulAddress);

static void usage(void)
{
	fprintf(stderr, 
		"usage:\n"
		"bin2mot.exe -i InputFile -o OutputFile [-s1]\n"
		"\t-i InputFile  : Binary file name.\n"
		"\t-o OutputFile : Mot file name.\n"
		"\t-s1           : Mot file S1 or S2 or S3.\n"
		"\t-V            : Display version information.\n");
	exit(1);
}

void write_mot_file(FILE *file, MOTREC_MODE ucMotRecMode, unsigned char *ucData, unsigned char ucSize, unsigned long ulAddress)
{
    unsigned char i;
    unsigned char checksum = 0x00;
    unsigned char ucRecData[2*REC_LINE_SIZE+1];
    unsigned char ucMotLine[MAX_LINE_SIZE];

    for( i = 0; i < ucSize; i++ )
    {
        checksum += ucData[i];
        sprintf(ucRecData+2*i, "%02X", ucData[i]);
    }
    ucRecData[2*ucSize] = '\0';

	switch( ucMotRecMode )
	{
		case MOTREC_MODE_S1:
			ucSize += 3;
			checksum += ucSize;
			checksum += (ulAddress >> 8);
			checksum += (ulAddress & 0xFF);
			checksum = 0xFF - checksum;
			sprintf(ucMotLine, "S1%02X%04X%s%02X\r\n", ucSize, ulAddress, ucRecData, checksum);
			break;

		case MOTREC_MODE_S2:
			ucSize += 4;
			checksum += ucSize;
			checksum += (ulAddress >> 16);
			checksum += (ulAddress >> 8);
			checksum += (ulAddress & 0xFF);
			checksum = 0xFF - checksum;
			sprintf(ucMotLine, "S2%02X%06X%s%02X\r\n", ucSize, ulAddress, ucRecData, checksum);
			break;
	
		case MOTREC_MODE_S3:
			ucSize += 5;
			checksum += ucSize;
			checksum += (ulAddress >> 24);
			checksum += (ulAddress >> 16);
			checksum += (ulAddress >> 8);
			checksum += (ulAddress & 0xFF);
			checksum = 0xFF - checksum;
			sprintf(ucMotLine, "S3%02X%08X%s%02X\r\n", ucSize, ulAddress, ucRecData, checksum);
			break;
	
		default:
			break;
	}
    /* WriteLength = Head + Address + Data + CheckSum + CRLF */
    /* ucSize = Address + Data */
    /* WriteLength = Head + ucSize + CheckSum + CRLF */
    /* WriteLength = 2 + 2*ucSize + 2 + 2 */
    /* WriteLength = 2*(ucSize + 3) */
    fwrite(ucMotLine, 2, ucSize+3, file);
}

int main (int argc, char *argv[])
{
    int i;
	FILE *filein;	/* input files */
	FILE *fileout;	/* output files */
	unsigned char ucBinaryData[REC_LINE_SIZE];
   	unsigned long ulReadSize = 0x00000000UL;
	unsigned long ulRecAddress = 0x00000000UL;
	unsigned long ulBinarySize = 0x00000000UL;
	MOTREC_MODE ucMotRecMode = MOTREC_MODE_UNKNOWN;
	char *pInputFile = NULL;	/* point to input file name */
	char *pOutputFile = NULL;	/* point to output file name */
	
	if( 1 == argc )
	{
		usage();
	}

	/* Analys parameter */
	for( i = 1; i < argc; i++ )
	{
		if( SEPERATOR == argv[i][0] )
		{
			switch( argv[i][1] )
			{
				/* input file name */
				case 'i':
					if( argv[i][2] != '\0' )
					{
						usage();
					}
					else
					{
						pInputFile = argv[++i];
					}
					break;
					
				/* output file name */
				case 'o':
					if( argv[i][2] != '\0' )
					{
						usage();
					}
					else
					{
						pOutputFile = argv[++i];
					}
					break;
					
				/* Mot file record type */
				case 's':
					if( argv[i][3] != '\0' )
					{
						usage();
					}
					else
					{
						switch( argv[i][2] )
						{
							case '1':
								ucMotRecMode = MOTREC_MODE_S1;
								break;

							case '2':
								ucMotRecMode = MOTREC_MODE_S2;
								break;

							case '3':
								ucMotRecMode = MOTREC_MODE_S3;
								break;

							case '0':
							case '5':
							case '7':
							case '8':
							case '9':
								fprintf(stdout, "Invalid record type \'%s\'!\n", argv[2]);
								usage();
								break;

							default:
								fprintf(stdout, "Invalid record type \'%s\'!\n", argv[2]);
								usage();
								break;
						}
					}
					break;

				/* Display version information */
				case 'V':
					if( argv[i][2] != '\0' )
					{
						usage();
					}
					else
					{
						fprintf(stdout, "bin2mot %s, Copyright (C) 2012 Pioneer Suntec.\n", version_info);
						exit(1);
					}
					break;
					
				default:
					usage();
					break;
			}
		}
	}

	if( NULL ==  pInputFile )
	{
		fprintf(stderr, "No input file name!\n");
		usage();
	}

	if( NULL == pOutputFile )
	{
		fprintf(stderr, "No output file name!\n");
		usage();
	}
	
	filein = fopen(pInputFile, "rb");
	if( NULL == filein )
	{
		fprintf(stderr, "Input file %s cannot be opened.", pInputFile);
		exit(1);
	}

	fileout = fopen(pOutputFile, "wb");
	if( NULL == fileout )
	{
		fprintf(stderr, "Output file %s cannot be opened.", pOutputFile);
		fclose (filein);
		exit(1);
	}

    fseek(filein, 0L, SEEK_END);
    ulBinarySize = ftell(filein);
    fseek(filein, 0L, SEEK_SET);
	if( MOTREC_MODE_UNKNOWN == ucMotRecMode )
	{
		if( ulBinarySize <= 0x10000 )
		{
			ucMotRecMode = MOTREC_MODE_S1;
		}
		else if( ulBinarySize <= 0x1000000 )
		{
			ucMotRecMode = MOTREC_MODE_S2;
		}
		else
		{
			ucMotRecMode = MOTREC_MODE_S3;
		}
	}

    ulReadSize = 0x00000000UL;
    while( (ulReadSize+REC_LINE_SIZE) < ulBinarySize )
    {
        fread(ucBinaryData, REC_LINE_SIZE, 1, filein);
        write_mot_file(fileout, ucMotRecMode, ucBinaryData, REC_LINE_SIZE, ulReadSize);
        ulReadSize += REC_LINE_SIZE;
    }
    fread(ucBinaryData, ulBinarySize-ulReadSize, 1, filein);
    write_mot_file(fileout, ucMotRecMode, ucBinaryData, ulBinarySize-ulReadSize, ulReadSize);

    fwrite("S804000000FB\r\n", 2, 7, fileout);

	fclose(filein);
	fclose(fileout);

	return 0;
}

